package code;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lixun
 * @version 1.0
 * @description: 进制转换
 * @date 2023/6/28 15:50
 */
public class Converter {

    private static final Map<Integer, Map<Character, Integer>> charsMap = new HashMap<>();

    private static final char[] CHARS_2 = new char[] {'0', '1'};
    private static final char[] CHARS_8 = new char[] {'0', '1', '2', '3', '4', '5', '6', '7'};
    private static final char[] CHARS_10 = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    private static final char[] CHARS_16 = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static final char[] CHARS_32 = new char[] {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
    };
    private static final char[] CHARS_64 = new char[] {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
    };

    static {
        charsMap.put(2, new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_2.length; i++)
                put(CHARS_2[i], i);
            put('+', -1);
            put('-', -1);
        }});
        charsMap.put(8, new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_8.length; i++)
                put(CHARS_8[i], i);
            put('+', -1);
            put('-', -1);
        }});
        charsMap.put(10,  new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_10.length; i++)
                put(CHARS_10[i], i);
            put('+', -1);
            put('-', -1);
        }});
        charsMap.put(16,  new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_16.length; i++)
                put(CHARS_16[i], i);
            put('+', -1);
            put('-', -1);
        }});
        charsMap.put(32,  new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_32.length; i++)
                put(CHARS_32[i], i);
            put('+', -1);
            put('-', -1);
        }});
        charsMap.put(64,  new HashMap<Character, Integer>(){{
            for (int i = 0; i < CHARS_64.length; i++)
                put(CHARS_64[i], i);
            put('-', -1);
        }});
    }


    public static int otherToDecimal(String s, int radix) {
        if (s == null || s.length() == 0) {
            throw new NumberFormatException("null");
        }
        if (!charsMap.containsKey(radix)) {
            throw new NumberFormatException("不支持" + radix + "进制转换");
        }
        if (!isQualified(s, radix)) {
            throw new NumberFormatException("待转换:"+ s + "， 不是" + radix + "进制");
        }
        Map<Character, Integer> map = charsMap.get(radix);
        // 负累积可避免接近MAX_VALUE的意外
        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;
        // 符号位
        char firstChar = s.charAt(0);
        if (firstChar == '-') {
            negative = true;
            limit = Integer.MIN_VALUE;
            i++;
        } else if (radix != 64 && firstChar == '+') {
            i++;
        }
        multmin = limit / radix;
        while (i < len) {
            digit = map.get(s.charAt(i++));
            if (digit < 0) {
                throw new NumberFormatException("符号位过多，"+s);
            }
            if (result < multmin) {
                throw new NumberFormatException("int数值溢出");
            }
            result *= radix;
            if (result < limit + digit) {
                throw new NumberFormatException("int数值溢出");
            }
            result -= digit;
        }
        return negative ? result : -result;
    }

    public static String decimalToOther(int decimal, int radix) {
        if (!charsMap.containsKey(radix)) {
            throw new NumberFormatException("不支持转换" + radix + "进制");
        }
        StringBuilder sb = new StringBuilder();
        boolean negative = decimal < 0;
        char[] chars = null;
        switch (radix) {
            case 2:
                chars = CHARS_2;
                break;
            case 8:
                chars = CHARS_8;
                break;
            case 10:
                chars = CHARS_10;
                break;
            case 16:
                chars = CHARS_16;
                break;
            case 32:
                chars = CHARS_32;
                break;
            case 64:
                chars = CHARS_64;
                break;
        }
        while (decimal != 0) {
            int i = decimal % radix;
            assert chars != null;
            sb.append(chars[Math.abs(i)]);
            decimal /= radix;
        }
        if (negative) sb.append('-');
        sb.reverse();
        return sb.toString();
    }

    public static long otherToDecimalLong(String s, int radix) {
        if (s == null || s.length() == 0) {
            throw new NumberFormatException("null");
        }
        if (!charsMap.containsKey(radix)) {
            throw new NumberFormatException("不支持" + radix + "进制转换");
        }
        if (!isQualified(s, radix)) {
            throw new NumberFormatException("待转换:"+ s + "， 不是" + radix + "进制");
        }
        Map<Character, Integer> map = charsMap.get(radix);
        // 负累积可避免接近MAX_VALUE的意外
        long result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        long limit = -Long.MAX_VALUE;
        long multmin;
        int digit;
        // 符号位
        char firstChar = s.charAt(0);
        if (firstChar == '-') {
            negative = true;
            limit = Long.MIN_VALUE;
            i++;
        } else if (radix != 64 && firstChar == '+') {
            i++;
        }
        multmin = limit / radix;
        while (i < len) {
            digit = map.get(s.charAt(i++));
            if (digit < 0) {
                throw new NumberFormatException("符号位过多，"+s);
            }
            if (result < multmin) {
                throw new NumberFormatException("int数值溢出");
            }
            result *= radix;
            if (result < limit + digit) {
                throw new NumberFormatException("int数值溢出");
            }
            result -= digit;
        }
        return negative ? result : -result;
    }

    public static String decimalToOther(long decimal, int radix) {
        if (!charsMap.containsKey(radix)) {
            throw new NumberFormatException("不支持转换" + radix + "进制");
        }
        StringBuilder sb = new StringBuilder();
        boolean negative = decimal < 0;
        char[] chars = null;
        switch (radix) {
            case 2:
                chars = CHARS_2;
                break;
            case 8:
                chars = CHARS_8;
                break;
            case 10:
                chars = CHARS_10;
                break;
            case 16:
                chars = CHARS_16;
                break;
            case 32:
                chars = CHARS_32;
                break;
            case 64:
                chars = CHARS_64;
                break;
        }
        while (decimal != 0) {
            int i = (int) (decimal % radix);
            assert chars != null;
            sb.append(chars[Math.abs(i)]);
            decimal /= radix;
        }
        if (negative) sb.append('-');
        sb.reverse();
        return sb.toString();
    }

    /**
     * 传入数据是否合法
     * @author lixun
     * @date 2023/6/29 11:15
     */
    public static boolean isQualified(String s, int radix) {
        Map<Character, Integer> map = charsMap.get(radix);
        if (map == null) {
            throw new NumberFormatException("不支持"+ radix + "进制");
        }
        List<Character> chars = new ArrayList<>(s.length());
        for (char c : s.toCharArray()) {
            chars.add(c);
        }
        return map.keySet().containsAll(chars);
    }
    public static void main(String[] args) {
        System.out.println((int) '-');
        System.out.println((int) ' ');
        System.out.println((int) '+');
        System.out.println((int) '0');
        System.out.println(otherToDecimal("-CAAAAA", 64));
        System.out.println(otherToDecimal("B/////", 64));
        System.out.println(-33 % 8);
        System.out.println(-7 % 8);
        System.out.println(Integer.MIN_VALUE);
        System.out.println(decimalToOther(Integer.MIN_VALUE, 10));
        System.out.println(decimalToOther(Integer.MIN_VALUE, 16));
        System.out.println(decimalToOther(Integer.MAX_VALUE, 64));
        System.out.println(decimalToOther(20000906L, 64));
        System.out.println(decimalToOther(469789L, 64));
        System.out.println(decimalToOther(20230208L, 64));
        System.out.println(otherToDecimalLong("BMQq3", 64));
        System.out.println(otherToDecimalLong("BMTCK", 64));
        System.out.println(otherToDecimalLong("CzLe5", 64));
    }
}
